﻿#region Copyright & licence

// This file is part of Refix.
// 
// Refix is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// 
// Refix is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with Refix.  If not, see <http://www.gnu.org/licenses/>.
// 
// Copyright (C) 2010-11 David Musgrove and others.

#endregion

using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

using Refix.Core;
using Refix.Core.Repository;
using Refix.RepositoryBrowser.Models;
using Refix.RepositoryBrowser.Presenters;
using Refix.RepositoryBrowser.Properties;
using Refix.RepositoryBrowser.Views;

namespace Refix.RepositoryBrowser
{
    public partial class BrowserForm : Form, IBrowserView
    {
        private IRepositoryModel _model;

        public IBrowserPresenter Presenter { get; set; }
        public IRepositoryModel Model
        {
            get { return _model; }
            set
            {
                _model = value;
                _model.RepositoryChanged += (o, e) => BindTreeViewToRepositoryModel();
                _model.RepositoryItemSelected += (o, e) => ShowVersionDetails(e.Item);
                _model.StatusReported += (o, e) => SetStatus(e.Value);
                _model.ErrorReported += ModelErrorReported;
            }
        }

        static private void ModelErrorReported(object sender, ValueEventArgs<string> e)
        {
            MessageBox.Show(e.Value, Resources.ERROR_ENCOUNTERED, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }

        private void BindTreeViewToRepositoryModel()
        {
            ClearTreeView();
            AddTreeViewNodesRecursive(null, Model.Repository);
            UseWaitCursor = false;
            Enabled = true;
        }

        private void AddTreeViewNodesRecursive(TreeNode parent, RepositoryItem repository)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<TreeNode, RepositoryItem>(AddTreeViewNodesRecursive), parent, repository);
                return;
            }
            TreeNodeCollection nodes = parent == null
                                           ? TreeView.Nodes
                                           : parent.Nodes;
            foreach (var repositoryItem in repository.Children)
            {
                var node = new TreeNode(repositoryItem.Name, (int)repositoryItem.State, (int)repositoryItem.State)
                           {Tag = repositoryItem};
                nodes.Add(node);
                AddTreeViewNodesRecursive(node, repositoryItem);
            }
        }

        public BrowserForm()
        {
            InitializeComponent();
            Model = new RepositoryModel();
            Presenter = new BrowserPresenter
                        {
                            Model = Model,
                            RepositoryModelFactory = new RepositoryModelFactory()
                        };
            SetDirectory(Configuration.LocalRepository);
        }

        private void SetDirectory(string directory) 
        {
            Enabled = false;
            UseWaitCursor = true;
            new Thread(() => Presenter.SetDirectory(directory)).Start();
        }

        private void SetVersionDetailsVisibility(bool visible)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<bool>(SetVersionDetailsVisibility), visible);
                return;
            }
            VersionDetailsPanel.Visible = visible;
            VersionDetailsListView.Visible = visible;
            DependenciesListView.Visible = visible;
            FileMenuAdd.Enabled = visible;
            if (!visible)
            {
                FileMenuRemove.Enabled = false;
            }
            TreeViewContextMenuRemove.Enabled = visible;
            FileMenuRemove.Enabled = visible;
        }

        private void ClearTreeView()
        {
            if (InvokeRequired)
            {
                Invoke(new Action(ClearTreeView));
                return;
            }
            TreeView.Nodes.Clear();
        }

        private void ClearListViews()
        {
            if (InvokeRequired)
            {
                Invoke(new Action(ClearListViews));
                return;
            }
            VersionDetailsListView.Items.Clear();
            DependenciesListView.Items.Clear();
        }

        private void SetComponentVersion(ComponentVersion componentVersion)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<ComponentVersion>(SetComponentVersion), componentVersion);
                return;
            }
            AlternativeVersion.Text = (componentVersion.AlternativeVersions
                                           .FirstOrDefault() ?? new AlternativeVersion()).Number;
            ReplacementVersion.Text = componentVersion.ReplaceWithVersion;
        }

        private void TreeViewContextMenuRemove_Click(object sender, EventArgs e)
        {
            RemoveVersionFromComponent();
        }

        private void RemoveVersionFromComponent()
        {
            if (MessageBox.Show(Resources.REMOVE_VERSION_CONFIRMATION_PROMPT, Resources.REMOVE_CONFIRMATION_CAPTION,
                MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                Presenter.RemoveRepositoryItem((RepositoryItem)TreeView.SelectedNode.Tag);
            }
        }

        private void VersionMenuUnregister_Click(object sender, EventArgs e)
        {
            RemoveVersionFromComponent();
        }

        private void UploadButton_Click(object sender, EventArgs e)
        {
            var repositoryItem = (RepositoryItem)TreeView.SelectedNode.Tag;
            if (Presenter.UploadRepositoryItem(repositoryItem))
            {
                string message =
                    string.Format(Resources.COMPONENT_SUCCESSFULLY_UPLOADED,
                                  repositoryItem.Parent.Component.Name, repositoryItem.ComponentVersion.Number,
                                  Configuration.RemoteRepository);
                MessageBox.Show(message, Resources.UPLOAD_SUCCESSFUL, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                string message =
                    string.Format(Resources.COMPONENT_COULD_NOT_BE_UPLOADED,
                                  repositoryItem.Parent.Component.Name, repositoryItem.ComponentVersion.Number,
                                  Configuration.RemoteRepository);
                MessageBox.Show(message, Resources.UPLOAD_UNSUCCESSFUL, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        private void AddListItem(Dependency dependency)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<Dependency>(AddListItem), dependency);
                return;
            }
            DependenciesListView.Items.Add(dependency.Name).SubItems.Add("Version").Text = dependency.Version;
        }

        private void AddListItem(FileInfo file)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<FileInfo>(AddListItem), file);
                return;
            }
            ListViewItem listViewItem = VersionDetailsListView.Items.Add(file.Name);
            listViewItem.SubItems.Add("Size").Text = file.Length.ToString("n0");
            listViewItem.SubItems.Add("Modified").Text = file.LastWriteTime.ToString("g");
        }

        private void TreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            ToggleFolderIcon(e.Node, true);
        }

        private void TreeView_AfterCollapse(object sender, TreeViewEventArgs e)
        {
            ToggleFolderIcon(e.Node, false);
        }

        private void ToggleFolderIcon(TreeNode node, bool open)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<TreeNode, bool>(ToggleFolderIcon), node, open);
                return;
            }
            if (open && node.ImageIndex == (int)RepositoryItemState.Folder)
            {
                node.ImageIndex = (int)RepositoryItemState.OpenFolder;
            }
            if (!open && node.ImageIndex == (int)RepositoryItemState.OpenFolder)
            {
                node.ImageIndex = (int)RepositoryItemState.Folder;
            }
        }

        private void FileMenuExit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void ShowVersionDetails(RepositoryItem item)
        {
            if (item.ComponentVersion == null)
            {
                SetVersionDetailsVisibility(false);
                return;
            }
            ClearListViews();
            SetComponentVersion(item.ComponentVersion);
            SetVersionDetailsVisibility(true);
            if (Directory.Exists(item.Directory))
            {
                var directoryInfo = new DirectoryInfo(item.Directory);
                foreach (var fileInfo in directoryInfo.GetFiles())
                {
                    AddListItem(fileInfo);
                }
                foreach (var dependency in item.ComponentVersion.Dependencies)
                {
                    AddListItem(dependency);
                }
            }
        }

        private void SaveChangesButton_Click(object sender, EventArgs e)
        {
            var repositoryItem = (RepositoryItem)TreeView.SelectedNode.Tag;
            repositoryItem.ComponentVersion.AlternativeVersions.Clear();
            repositoryItem.ComponentVersion.ReplaceWithVersion = null;
            if (!AlternativeVersion.Text.IsNullOrEmpty())
            {
                repositoryItem.ComponentVersion.AlternativeVersions.Add(new AlternativeVersion { Number = AlternativeVersion.Text });
            }
            if (!ReplacementVersion.Text.IsNullOrEmpty())
            {
                repositoryItem.ComponentVersion.ReplaceWithVersion = ReplacementVersion.Text;
            }
            Presenter.SaveRepositoryItem(repositoryItem);
        }

        private void TreeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            Presenter.SelectRepositoryItem((RepositoryItem)e.Node.Tag);
        }

        private void FileMenuChangeDirectory_Click(object sender, EventArgs e)
        {
            DialogResult result = FolderBrowserDialog.ShowDialog();
            if (result == DialogResult.OK)
            {
                SetDirectory(FolderBrowserDialog.SelectedPath);
            }
        }

        private void HelpMenuAbout_Click(object sender, EventArgs e)
        {
            new AboutBox().ShowDialog();
        }

        private void ViewMenuRefresh_Click(object sender, EventArgs e)
        {
            SetDirectory(Model.Repository.RootDirectory);
        }

        private void TreeView_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                TreeView.SelectedNode = TreeView.GetNodeAt(e.X, e.Y);
            }
        }

        private void AlternativeVersion_TextChanged(object sender, EventArgs e)
        {
            SaveChangesButton.Enabled = true;
        }

        private void ReplacementVersion_TextChanged(object sender, EventArgs e)
        {
            SaveChangesButton.Enabled = true;
        }

        private void SetStatus(string status)
        {
            if (status.IsNullOrEmpty() && !Model.Repository.RootDirectory.IsNullOrEmpty())
            {
                SetStatus(Model.Repository.RootDirectory);
                return;
            }
            if (InvokeRequired)
            {
                Invoke(new Action<string>(SetStatus), status);
                return;
            }
            StatusLabel.Text = status;
            StatusStrip.Refresh();
        }

        private void FileListViewContextMenuAdd_Click(object sender, EventArgs e)
        {
            AddFileToVersion();
        }

        private void FileMenuAdd_Click(object sender, EventArgs e)
        {
            AddFileToVersion();
        }

        private void AddFileToVersion()
        {
            OpenFileDialog.Filter = Resources.FILTER_ALL_FILES;
            DialogResult result = OpenFileDialog.ShowDialog();
            if (result == DialogResult.OK)
            {
                Presenter.AddFileToRepository((RepositoryItem)TreeView.SelectedNode.Tag, OpenFileDialog.FileName);
            }
        }

        private void FileListViewContextMenuRemove_Click(object sender, EventArgs e)
        {
            RemoveFileFromVersion();
        }

        private void FileMenuRemove_Click(object sender, EventArgs e)
        {
            RemoveFileFromVersion();
        }

        private void RemoveFileFromVersion()
        {
            if (MessageBox.Show(Resources.REMOVE_FILE_CONFIRMATION_PROMPT, Resources.REMOVE_CONFIRMATION_CAPTION,
                MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                Presenter.RemoveFileFromRepository((RepositoryItem)TreeView.SelectedNode.Tag, VersionDetailsListView.SelectedItems[0].Text);
            }
        }

        private void FileListViewContextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            SetRemoveMenuItemAvailability();
        }

        private void VersionDetailsListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            SetRemoveMenuItemAvailability();
        }

        private void SetRemoveMenuItemAvailability()
        {
            if (InvokeRequired)
            {
                Invoke(new Action(SetRemoveMenuItemAvailability));
                return;
            }
            FileListViewContextMenuRemove.Enabled = VersionDetailsListView.SelectedIndices.Count == 1;
            FileMenuRemove.Enabled = VersionDetailsListView.SelectedIndices.Count == 1;
        }

        private void VersionMenuRegister_Click(object sender, EventArgs e)
        {
            OpenFileDialog.Filter = Resources.FILTER_LIBRARIES_EXECUTABLES;    
            DialogResult result = OpenFileDialog.ShowDialog();
            if (result == DialogResult.OK)
            {
                string assemblyFile = OpenFileDialog.FileName;
                if (Presenter.RegisterRepositoryAssembly(assemblyFile))
                {
                    SetDirectory(Model.Repository.RootDirectory);
                }
            }
        }

        private void SynchronizeButton_Click(object sender, EventArgs e)
        {
            var repositoryItem = (RepositoryItem)TreeView.SelectedNode.Tag;
            Presenter.SynchronizeRepositoryItem(repositoryItem);
        }
    }
}